package recordProcessing;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

import main.Main;

import patternRecognition.QueryForHorizontalRecords;
import patternRecognition.QueryForVerticalRecords;
import patternRecognition.QueryWithoutStructure;

import table.Table;
import userInterface.GUIController;

import fields.AbstractField;
import fields.FieldMatcher;
import fileProcessing.ParsingException;
import fileProcessing.ProjectDirectories;
import fileProcessing.PreprocessingAnalysis.FileAnalysis;

/**
 * Parses all preprocessed files to build records based on the contents.
 * @author Andrew
 *
 */
public class RecordParser {
	public static int threadCount=0;
	private ArrayList<String> fieldDefinitions = new ArrayList<String>();//use for header fields
	private ArrayList<File> files;
	private ArrayList<Integer> headerLines;
	private ArrayList<String> headerFieldsIdentified;
	
	public RecordParser(FileAnalysis fileStructures) throws ParsingException{
		files = fileStructures.getPreprocessingFiles();
		headerLines = fileStructures.getHeaderLineResults();
		headerFieldsIdentified = fileStructures.getHeaderLineMatches();
		execute();
	}
	
	public RecordParser() {}

	private void execute() throws ParsingException{
		int headerLine,i = 0,j=1;//index for header line list
		GUIController.getMainPage().setStatus("Parsing file(s)...\n");
		Main.audit.setPrevTime();//start the clock to record the parsing run times.
		for(File f : files){
			GUIController.getMainPage().setStatus("Parsing records from source #" + j + " ...");
			j++;
			headerLine = headerLines.get(i);
			BufferedReader file;
			try {
				file = new BufferedReader(new FileReader(f));
			} catch (FileNotFoundException e) {
				throw new ParsingException("File not found error when opening "+f+" for parsing.");
			}
			try {
				Main.audit.writeToAudit("Parsing: "+f.getCanonicalPath());
				if(headerLine != 0){
					parseWithHeader(f.getName(),file,headerLine,headerFieldsIdentified.get(i));
				}
				else{
					parseWithoutHeader(f);
				}
			
				Main.audit.writeExecutionTime("Parsing time: ");
			} catch (IOException e) {
				throw new ParsingException("Error getting canonical file name.");
			}
			i++;
		}
	}//end execute
	
	/**
	 * Parse the file based on the expectation that the data will follow a defined structure
	 * according to the fields found in the header line. Creates a table to store the records and after the 
	 * file has been completely parsed the entries will be converted to records and stored into the record collection.
	 * @param file
	 * @param headerLine
	 * @param headerFields
	 * @throws ParsingException
	 */
	private void parseWithHeader(String fileName, BufferedReader file, 
			int headerLine, String headerFields) throws ParsingException {
		System.out.println("parsing with header");
		String tableName = fileName.substring(0, fileName.indexOf("."));
		String fieldDelimiter = getDelimiter(headerFields);
		Table table;
		ArrayList<AbstractField> fieldList = new ArrayList<AbstractField>();
		int exceptionCatcher;

		for(String s : getTokenizedLine(headerFields,fieldDelimiter)){
			fieldList.add(FieldMatcher.getField(s));
		}
		table = new Table(tableName,fieldList);
		try {
			int i = 0;
			String line = file.readLine();
			while(line != null){
				System.out.print(line + " new\n");
				if(i<2)
					table.addFileCollectionData(line);
				else if(i >= headerLine){
					exceptionCatcher = 1;
					while(exceptionCatcher != 0){
						try{
							table.addData(getTokenizedLine(line,fieldDelimiter));
							exceptionCatcher = 0;
						} catch (ParsingException e) {
							if(exceptionCatcher > 4){
								Main.audit.writeExecutionTime(e.getMessage());
								exceptionCatcher = 0;
							}
							else
								exceptionCatcher++;
						}	
					}
				}
				
				line = file.readLine();
				i++;
			}
			Main.tCollection.addTable(table);
		} catch (IOException e) {
			throw new ParsingException("Error parsing file "+file);
		}
		GUIController.getMainPage().setStatus("Extracted " + table.getRecordCount() + " records.\n");
		GUIController.getMainPage().task.notify(1f/files.size() * 45);
		Main.audit.writeToAudit("Extracted: " + table.getRecordCount() + " records.");
		
	}
	
	private void parseWithoutHeader(File f) throws ParsingException {
		String tableName = f.getName().substring(0, f.getName().indexOf("."));
		int vCount,hCount,qCount;
		
		//declare queries and read in file, perform analysis.
		QueryForVerticalRecords qfvr = new QueryForVerticalRecords(f);
		QueryForHorizontalRecords qfhr = new QueryForHorizontalRecords(f);
		QueryWithoutStructure qws = new QueryWithoutStructure(f,ProjectDirectories.outputFileDir);
		threadCount=0;
		Thread t1 = new Thread(qfvr);
		Thread t2 = new Thread(qfhr);
		t1.start();
		t2.start();
		
		
		while(threadCount<2){
			System.out.print("");
		}
		
		if(qfvr.getFields().size() == 0)
			vCount = 0;
		else
			vCount = (qfvr.getData().size()/qfvr.getFields().size());
		
		
		if(qfhr.getFields().size() == 0)
			hCount = 0;
		else
			hCount = (qfhr.getData().size()/qfhr.getFields().size());
		
		//Initialize table
		Table table = new Table(tableName);
		table.addFileCollectionData(qfvr.getCollectionSource());
		table.addFileCollectionData(qfvr.getCollectionDate());
		
		//rank queries based on the number of records found
		int rank[] = {vCount,hCount};
		if(rank[0] == 0 && rank[1] == 0){//if vertical and horizontal queries found no records 
			GUIController.getMainPage().setStatus("No structured data found. \n\tSearch results saved in output folder\n");
			new Thread(qws).start();
		}
		else if(rank[0] > rank[1]){//vertical query best
			Main.audit.writeToAudit("Vertical Query is chosen.");
			for(String field : qfvr.getFields()){//add fields to the table
				table.addField(FieldMatcher.getField(field));
			}
			table.addData(toStringArray(qfvr.getData()));
			Main.tCollection.addTable(table);
		}
		else if((rank[1] > rank[0]) || (rank[0] == rank[1] && rank[0] > 0)){//horizontal query best
			Main.audit.writeToAudit("Horizontal Query is chosen.");
			for(String field : qfhr.getFields()){//add fields to the table
				table.addField(FieldMatcher.getField(field));
			}
			table.addData(toStringArray(qfhr.getData()));
			Main.tCollection.addTable(table);
		}
		else{//None of the queries identified records
			Main.audit.writeToAudit("Notice: No records found in file");
		}
		GUIController.getMainPage().setStatus("Extracted: " + table.getRecordCount() + " records.\n");
		Main.audit.writeToAudit("Extracted: " + table.getRecordCount() + " records.");
	}		
	
	public static int countOf(String o,ArrayList<String> list){
		int count=0;
		if(list.contains(o)){
			for(String temp : list){
				if(temp.equals(o))
					count++;
			}
		}
		return count;
	}
	
	public String getDelimiter(String line){
		String temp = line.trim();
		temp = temp.replaceAll("\\s+", " ");
		int barDel = temp.split("\\|").length;
		int comDel = temp.split(",").length;
		int tabDel = temp.split("\\t").length;
		if(barDel > 1)
			return "\\|";
		else if(comDel > 1)
			return ",";
		else if(tabDel > 1)
			return "\\t";
		else
			return "\\s";
	}
	
	/**
	 * Tokenizes a line as well as removes commas,extra spaces, quotes and apostrophes.
	 * @param line
	 * @return
	 */
	public String[] getTokenizedLine(String line,String delimiter){
		String temp = line.trim();
		temp = temp.replaceAll("\\s+", " ");
		temp = temp.replaceAll("\"", "");
		temp = temp.replaceAll("'", "");
		return temp.split(delimiter);
	}
	
	public String getConditionedLine(String[] line){
		String result = "";
		int i=1;
		for(String word : line){
			if(i<line.length)
				result += word+' ';
			else
				result += word;
		}
		return result;
	}
	
	public String[] toStringArray(ArrayList<String> inList){
		String[] result = new String[inList.size()];
		int index=0;
		for(String s : inList){
			result[index] = s;
			index++;
		}
		return result;
	}	
}//end class
